#ifndef TINX_MEMORY_H
#define TINX_MEMORY_H

#include <tinx/assert.h>
#include <tinx/types.h>

// 内核内存大小 16 MB
#define KERNEL_MEMORY_SIZE 0x1000000

// 内存虚拟盘起始地址
#define KERNEL_RAMDISK_ADDR 0xc00000

// 内粗虚拟盘大小 4 MB
#define KERNEL_RAMDISK_SIZE 0x400000

// 用户内存起始地址
#define USER_EXEC_ADDR 0x8048000

// 用户内存末尾 3 GB
#define USER_MEMORY_END 0xC0000000

// 用户进程栈顶 3 GB
#define USER_STACK_TOP USER_MEMORY_END

// 用户栈大小 2 MB
#define USER_STACK_SIZE 0x200000

// 用户进程栈底
#define USER_STACK_BOTTOM (USER_STACK_TOP - USER_STACK_SIZE)

// 用户映射内存地址
#define USER_MMAP_ADDR 0x80000000

// 用户映射内存大小 256 MB
#define USER_MMAP_SIZE (PAGE_SIZE * PAGE_SIZE * 8)

// 用户映射内存末尾
#define USER_MMAP_END (USER_MMAP_ADDR + USER_MMAP_SIZE)

// 用户 interp 的加载地址，先用着吧
#define USER_INTERP_ADDR 0x40000000

#define PAGE_SIZE 0x1000

#define ASSERT_PAGE(addr) assert((addr & 0xfff) == 0)

#define PAGE_MASK (0xfff)

#define PDE_MASK (0xffc00000)

#define PAGE(idx) ((idx) << 12)
#define IDX(page) ((page) >> 12)

#define DIDX(vaddr) ((vaddr >> 22) & 0x3ff)
#define TIDX(vaddr) ((vaddr >> 12) & 0x3ff)

typedef struct page_entry_t
{
    u8 present : 1;  // 在内存中
    u8 write : 1;    // 0 只读 1 可读可写
    u8 user : 1;     // 1 所有人 0 超级用户 DPL < 3
    u8 pwt : 1;      // page write through 1 直写模式，0 回写模式
    u8 pcd : 1;      // page cache disable 禁止该页缓冲
    u8 accessed : 1; // 被访问过，用于统计使用频率
    u8 dirty : 1;    // 脏页，表示该页缓冲被写过
    u8 pat : 1;      // page attribute table 页大小 4K/4M
    u8 global : 1;   // 全局，所有进程都用到了，该页不刷新缓冲
    u8 shared : 1;   // 共享内存页，与 CPU 无关
    u8 reserved : 2;
    u32 index : 20;  // 页索引
} _packed page_entry_t;

typedef struct swap_entry_t
{
    u8 present : 1; // 一定为 0
    u32 index : 31;
} _packed swap_entry_t;

u32 alloc_kpage(u32 count);
void free_kpage(u32 pages, u32 count);

u32 get_page();
void put_page(u32 page);

u32 get_paddr(u32 vaddr);
u32 pte_vaddr(page_entry_t *entry);
page_entry_t *get_pte(u32 vaddr, bool create);

void set_cr3(u32 cr3);
u32 get_cr3();

page_entry_t *copy_pgdir();
void free_pgdir();

u32 link_page(u32 vaddr);
void unlink_page(u32 vaddr);

int sys_brk(u32 newbrk);

void map_page(u32 vaddr, u32 paddr, u32 count);
void unmap_page(u32 vaddr, u32 count);
void map_area(u32 vaddr, u32 len);

void entry_init(page_entry_t *entry, u32 paddr);
void entry_free(page_entry_t *entry);
void flush_tlb(u32 vaddr);

int sys_mmap(void *addr, size_t length, int prot, int flags, int fd, int offset);
int sys_munmap(void *addr, size_t length);

#endif